/*!
 * \file TimeUtils.hpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 时间处理工具类的封装
 * 
 * \details 提供全面的时间处理功能，包括：
 *          - 时间获取：高精度本地时间、UTC时间等
 *          - 时间转换：时间戳转换、格式化输出等
 *          - 时间计算：日期偏移、时间差计算等
 *          - 时间工具：周末判断、时区偏移计算等
 *          - 性能计时：高精度计时器实现
 *          
 *          该工具类针对交易系统的时间需求进行优化，
 *          提供毫秒级精度的时间处理能力，
 *          支持跨平台的高性能时间操作。
 */
#pragma once
#include <stdint.h>
#include <sys/timeb.h>
#ifdef _MSC_VER
#include <time.h>
#else
#include <sys/time.h>
#endif
#include <string>
#include <string.h>
#include<chrono>

#ifdef _MSC_VER
#define CTIME_BUF_SIZE 64

#define WIN32_LEAN_AND_MEAN

#include <windows.h>

/*!
 * \brief Windows内核系统时间结构
 * 
 * \details 用于高精度时间获取的内核级时间结构
 */
typedef struct _KSYSTEM_TIME
{
	ULONG LowPart;         ///< 时间低位部分
	LONG High1Time;        ///< 时间高位部分1
	LONG High2Time;        ///< 时间高位部分2
} KSYSTEM_TIME, *PKSYSTEM_TIME;

/*!
 * \brief Windows用户共享数据结构
 * 
 * \details 包含系统时间、中断时间、时区偏移等信息的共享数据结构
 */
struct KUSER_SHARED_DATA
{
	ULONG TickCountLowDeprecated;       ///< 已废弃的低位计数
	ULONG TickCountMultiplier;          ///< 计数乘数
	volatile KSYSTEM_TIME InterruptTime; ///< 中断时间
	volatile KSYSTEM_TIME SystemTime;   ///< 系统时间
	volatile KSYSTEM_TIME TimeZoneBias; ///< 时区偏移
};

#define KI_USER_SHARED_DATA   0x7FFE0000
#define SharedUserData   ((KUSER_SHARED_DATA * const)KI_USER_SHARED_DATA)

#define TICKSPERSEC        10000000L  ///< 每秒的时钟周期数
#endif

/*!
 * \brief 时间工具类
 * 
 * \details 提供全面的时间处理静态方法集合，包括：
 *          - 高精度时间获取和转换
 *          - 时间格式化和字符串处理
 *          - 日期时间计算和偏移
 *          - 时区处理和周末判断
 *          - 性能计时和时间差测量
 *          
 *          针对金融交易系统优化，提供毫秒级精度的时间操作。
 *          支持Windows和Linux跨平台高性能实现。
 */
class TimeUtils 
{
	
public:
	/*!
	 * \brief 获取本地时间（旧版本实现）
	 * \return 当前本地时间的毫秒时间戳
	 * 
	 * \details 使用ftime函数获取当前本地时间，精度为毫秒级。
	 *          这是较老的实现方式，建议使用getLocalTimeNow()。
	 */
	static inline int64_t getLocalTimeNowOld(void)
	{
		struct timeval tv;
		gettimeofday(&tv, nullptr);
		return tv.tv_sec * 1000 + tv.tv_usec / 1000;
	}

	/*!
	 * \brief 获取本地时间，精确到毫秒
	 * \return 当前本地时间的毫秒时间戳
	 * 
	 * \details 高性能的本地时间获取方法：
	 *          - Windows：使用共享用户数据区获取高精度系统时间
	 *          - Linux：使用ftime函数获取时间
	 *          返回1970年1月1日以来的毫秒数
	 */
	static inline int64_t getLocalTimeNow(void)
	{
#ifdef _MSC_VER
		LARGE_INTEGER SystemTime;
		do
		{
			SystemTime.HighPart = SharedUserData->SystemTime.High1Time;
			SystemTime.LowPart = SharedUserData->SystemTime.LowPart;
		} while (SystemTime.HighPart != SharedUserData->SystemTime.High2Time);

		uint64_t t = SystemTime.QuadPart;
		t = t - 11644473600L * TICKSPERSEC;
		return t / 10000;
#else
		struct timeval tv;
		gettimeofday(&tv, nullptr);
		return tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
	}

	/*!
	 * \brief 获取格式化的本地时间字符串
	 * \param bIncludeMilliSec 是否包含毫秒部分，默认为true
	 * \return 格式化的时间字符串
	 * 
	 * \details 返回格式化的时间字符串：
	 *          - 包含毫秒：HH:MM:SS,mmm（如：14:30:25,123）
	 *          - 不含毫秒：HH:MM:SS（如：14:30:25）
	 */
	static inline std::string getLocalTime(bool bIncludeMilliSec = true)
	{
		uint64_t ltime = getLocalTimeNow();
		time_t now = ltime / 1000;
		uint32_t millitm = ltime % 1000;
		tm * tNow = localtime(&now);

		char str[64] = {0};
		if(bIncludeMilliSec)
			snprintf(str, sizeof(str), "%02d:%02d:%02d,%03d", tNow->tm_hour, tNow->tm_min, tNow->tm_sec, millitm);
		else
			snprintf(str, sizeof(str), "%02d:%02d:%02d", tNow->tm_hour, tNow->tm_min, tNow->tm_sec);
		return str;
	}

	/*!
	 * \brief 获取YYYYMMDDHHMMSS格式的时间
	 * \return 14位数字格式的时间戳
	 * 
	 * \details 返回当前时间的紧凑格式表示，如：20220309143025
	 *          格式：YYYYMMDDHHMMSS（年月日时分秒）
	 */
	static inline uint64_t getYYYYMMDDhhmmss()
	{
		uint64_t ltime = getLocalTimeNow();
		time_t now = ltime / 1000;

		tm * tNow = localtime(&now);

		uint64_t date = (tNow->tm_year + 1900) * 10000 + (tNow->tm_mon + 1) * 100 + tNow->tm_mday;

		uint64_t time = tNow->tm_hour * 10000 + tNow->tm_min * 100 + tNow->tm_sec;
		return date * 1000000 + time;
	}

	/*!
	 * \brief 获取当前时间的日期和时间分量
	 * \param date 输出参数，当前日期，格式如20220309
	 * \param time 输出参数，当前时间，精确到毫秒，格式如103029500
	 * 
	 * \details 将当前时间分解为日期和时间两个部分：
	 *          - date：YYYYMMDD格式的日期
	 *          - time：HHMMSSMMM格式的时间（包含毫秒）
	 */
	static inline void getDateTime(uint32_t &date, uint32_t &time)
	{
		uint64_t ltime = getLocalTimeNow();
		time_t now = ltime / 1000;
		uint32_t millitm = ltime % 1000;

		tm * tNow = localtime(&now);

		date = (tNow->tm_year+1900)*10000 + (tNow->tm_mon+1)*100 + tNow->tm_mday;
		
		time = tNow->tm_hour*10000 + tNow->tm_min*100 + tNow->tm_sec;
		time *= 1000;
		time += millitm;
	}

	/*!
	 * \brief 获取当前日期
	 * \return YYYYMMDD格式的当前日期
	 * 
	 * \details 返回当前日期的8位数字表示，如：20220309
	 */
	static inline uint32_t getCurDate()
	{
		uint64_t ltime = getLocalTimeNow();
		time_t now = ltime / 1000;
		uint32_t millitm = ltime % 1000;

		tm * tNow = localtime(&now);

		uint32_t date = (tNow->tm_year+1900)*10000 + (tNow->tm_mon+1)*100 + tNow->tm_mday;

		return date;
	}

	/*!
	 * \brief 获取指定日期的星期几
	 * \param uDate 指定的日期，YYYYMMDD格式，0表示当前日期
	 * \return 星期几（0=星期日，1=星期一，...，6=星期六）
	 * 
	 * \details 返回指定日期是星期几：
	 *          - 0：星期日
	 *          - 1：星期一
	 *          - ...
	 *          - 6：星期六
	 */
	static inline uint32_t getWeekDay(uint32_t uDate = 0)
	{
		time_t ts = 0;
		if(uDate == 0)
		{
			ts = getLocalTimeNow()/1000;
		}
		else
		{
			tm t;	
			memset(&t,0,sizeof(tm));
			t.tm_year = uDate/10000 - 1900;
			t.tm_mon = (uDate%10000)/100 - 1;
			t.tm_mday = uDate % 100;
			ts = mktime(&t);
		}

		tm * tNow = localtime(&ts);
	
		return tNow->tm_wday;
	}

	/*!
	 * \brief 获取当前时间（HHMMSS格式）
	 * \return HHMMSS格式的当前时间
	 * 
	 * \details 返回当前时间的6位数字表示，如：143025（14:30:25）
	 */
	static inline uint32_t getCurMin()
	{
		uint64_t ltime = getLocalTimeNow();
		time_t now = ltime / 1000;
		uint32_t millitm = ltime % 1000;

		tm * tNow = localtime(&now);

		uint32_t time = tNow->tm_hour*10000 + tNow->tm_min*100 + tNow->tm_sec;

		return time;
	}

	/*!
	 * \brief 获取时区偏移量
	 * \return 时区偏移量（小时）
	 * 
	 * \details 计算本地时区相对于UTC的偏移量，单位为小时。
	 *          正数表示东时区，负数表示西时区。
	 *          结果会被缓存以提高性能。
	 */
	static inline int32_t getTZOffset()
	{
		static int32_t offset = 99;
		if(offset == 99)
		{
			time_t now = time(NULL);
			tm tm_ltm = *localtime(&now);
			tm tm_gtm = *gmtime(&now);

			time_t _gt = mktime(&tm_gtm);
			tm _gtm2 = *localtime(&_gt);

			offset = (uint32_t)(((now - _gt) + (_gtm2.tm_isdst ? 3600 : 0)) / 60);
			offset /= 60;
		}

		return offset;
	}

	/*!
	 * \brief 根据日期和时间创建时间戳
	 * \param lDate 日期，YYYYMMDD格式
	 * \param lTimeWithMs 时间，HHMMSSMMM格式（包含毫秒）
	 * \param isGM 是否为GMT时间，默认为false（本地时间）
	 * \return 对应的毫秒时间戳
	 * 
	 * \details 将指定的日期和时间组合成完整的时间戳：
	 *          - isGM为false：返回本地时间戳
	 *          - isGM为true：返回GMT时间戳
	 */
	static inline int64_t makeTime(long lDate, long lTimeWithMs, bool isGM = false)
	{
		tm t;	
		memset(&t,0,sizeof(tm));
		t.tm_isdst = -1;
		t.tm_year = lDate/10000 - 1900;
		t.tm_mon = (lDate%10000)/100 - 1;
		t.tm_mday = lDate % 100;

		int lTime = lTimeWithMs / 1000;
		int nMilli = lTimeWithMs % 1000;

		t.tm_hour = lTime / 10000;
		t.tm_min = (lTime%10000) / 100;
		t.tm_sec = lTime % 100;

		time_t result;
		if(isGM)
		{
#ifdef _MSC_VER
			result = _mkgmtime(&t);
#else
			result = timegm(&t);
#endif
		}
		else
		{
			result = mktime(&t);
		}

		return result * 1000 + nMilli;
	}

	/*!
	 * \brief 将时间戳转换为字符串
	 * \param mytime 毫秒时间戳
	 * \return 格式化的时间字符串
	 * 
	 * \details 将毫秒时间戳转换为可读的时间字符串格式。
	 *          包含毫秒精度的完整时间表示。
	 */
	static std::string timeToString(int64_t mytime)
	{
		char tm_buf[64] = { 0 };
		time_t ts = mytime / 1000;
		uint32_t _msec = mytime % 1000;
		struct tm t;
		memset(&t, 0, sizeof(tm));
#ifdef _MSC_VER
		localtime_s(&t, &ts);
#else
		t = *localtime(&ts);
#endif
		if (_msec > 0)
		{
			snprintf(tm_buf, sizeof(tm_buf), "%4d%02d%02d%02d%02d%02d.%03d",t.tm_year+1900, t.tm_mon+1, t.tm_mday,
				t.tm_hour, t.tm_min, t.tm_sec, _msec);
		}
		else
		{
			snprintf(tm_buf, sizeof(tm_buf), "%4d%02d%02d%02d%02d%02d",t.tm_year+1900, t.tm_mon+1, t.tm_mday,
				t.tm_hour, t.tm_min, t.tm_sec);
		}
		return tm_buf;
	}

	/*!
	 * \brief 获取指定日期之后的日期
	 * \param curDate 当前日期，YYYYMMDD格式
	 * \param days 偏移天数，默认为1天
	 * \return 偏移后的日期，YYYYMMDD格式
	 * 
	 * \details 计算指定日期之后若干天的日期，自动处理月份和年份的进位
	 */
	static uint32_t getNextDate(uint32_t curDate, int days = 1)
	{
		tm t;	
		memset(&t,0,sizeof(tm));
		t.tm_isdst = -1;
		t.tm_year = curDate/10000 - 1900;
		t.tm_mon = (curDate%10000)/100 - 1;
		t.tm_mday = curDate % 100;

		time_t ts = mktime(&t);
		ts += days*24*3600;
		tm* newT = localtime(&ts);

		return (newT->tm_year+1900)*10000 + (newT->tm_mon+1)*100 + newT->tm_mday;
	}

	/*!
	 * \brief 获取指定时间之后的时间
	 * \param curTime 当前时间，HHMMSS格式
	 * \param mins 偏移分钟数，默认为1分钟
	 * \return 偏移后的时间，HHMMSS格式
	 * 
	 * \details 计算指定时间之后若干分钟的时间，自动处理小时的进位。
	 *          注意：此方法不处理日期变化，只处理时间部分。
	 */
	static uint32_t getNextMinute(int32_t curTime, int32_t mins = 1)
	{
		int32_t hour = curTime / 10000;
		int32_t minute = (curTime % 10000) / 100;
		int32_t sec = curTime % 100;

		minute += mins;
		if(minute >= 60)
		{
			hour += minute / 60;
			minute = minute % 60;
		}

		if(hour >= 24)
		{
			hour = hour % 24;
		}

		return hour * 10000 + minute * 100 + sec;
	}

	/*!
	 * \brief 获取指定月份之后的月份
	 * \param curMonth 当前月份，YYYYMM格式
	 * \param months 偏移月数，默认为1个月
	 * \return 偏移后的月份，YYYYMM格式
	 * 
	 * \details 计算指定月份之后若干个月的月份，自动处理年份的进位
	 */
	static uint32_t getNextMonth(uint32_t curMonth, int months = 1)
	{
		uint32_t year = curMonth / 100;
		uint32_t month = curMonth % 100;

		month += months;
		if(month > 12)
		{
			uint32_t years = (month - 1) / 12;
			year += years;
			month = ((month - 1) % 12) + 1;
		}
		else if(month <= 0)
		{
			uint32_t years = (-month) / 12 + 1;
			year -= years;
			month = 12 - ((-month) % 12);
		}

		return year * 100 + month;
	}

	/*!
	 * \brief 将日期时间转换为分钟级时间条
	 * \param uDate 日期，YYYYMMDD格式
	 * \param uTime 时间，HHMM格式
	 * \return 分钟级时间条ID
	 * 
	 * \details 将日期和时间合并为用于分钟级K线的时间标识
	 */
	static inline uint64_t timeToMinBar(uint32_t uDate, uint32_t uTime)
	{
		return (uint64_t)uDate * 10000 + uTime;
	}

	/*!
	 * \brief 从分钟级时间条提取日期
	 * \param minTime 分钟级时间条ID
	 * \return YYYYMMDD格式的日期
	 */
	static inline uint32_t minBarToDate(uint64_t minTime)
	{
		return (uint32_t)(minTime / 10000);
	}

	/*!
	 * \brief 从分钟级时间条提取时间
	 * \param minTime 分钟级时间条ID
	 * \return HHMM格式的时间
	 */
	static inline uint32_t minBarToTime(uint64_t minTime)
	{
		return (uint32_t)(minTime % 10000);
	}

	/*!
	 * \brief 判断指定日期是否为周末
	 * \param uDate 日期，YYYYMMDD格式
	 * \return true表示周末，false表示工作日
	 * 
	 * \details 判断指定日期是否为周六或周日
	 */
	static inline bool isWeekends(uint32_t uDate)
	{
		tm t;	
		memset(&t,0,sizeof(tm));
		t.tm_year = uDate/10000 - 1900;
		t.tm_mon = (uDate%10000)/100 - 1;
		t.tm_mday = uDate % 100;
		time_t ts = mktime(&t);

		tm * tNow = localtime(&ts);

		return (tNow->tm_wday == 0 || tNow->tm_wday == 6);
	}

public:
	/*!
	 * \brief 32位时间类
	 * 
	 * \details 提供时间的面向对象封装，支持：
	 *          - 时间的创建和初始化
	 *          - 日期和时间的分别获取
	 *          - 毫秒精度的时间处理
	 *          - 本地时间的快速转换
	 */
	class Time32
	{
	public:
		/*!
		 * \brief 默认构造函数
		 * 
		 * \details 创建一个毫秒数为0的时间对象
		 */
		Time32():_msec(0){}

		/*!
		 * \brief 构造函数
		 * \param _time 时间戳（秒）
		 * \param msecs 毫秒部分，默认为0
		 * 
		 * \details 根据时间戳和毫秒数创建时间对象
		 */
		Time32(time_t _time, uint32_t msecs = 0)
		{
#ifdef _MSC_VER
			localtime_s(&t, &_time);
#else
			t = *localtime(&_time);
#endif
			_msec = msecs;
		}

		/*!
		 * \brief 构造函数
		 * \param _time 毫秒时间戳
		 * 
		 * \details 根据毫秒时间戳创建时间对象
		 */
		Time32(uint64_t _time)
		{
			time_t ts = _time / 1000;
			_msec = _time % 1000;
#ifdef _MSC_VER
			localtime_s(&t, &ts);
#else
			t = *localtime(&ts);
#endif
		}

		/*!
		 * \brief 从本地时间初始化
		 * \param _time 毫秒时间戳
		 * 
		 * \details 使用本地毫秒时间戳初始化时间对象
		 */
		void from_local_time(uint64_t _time)
		{
			time_t ts = _time / 1000;
			_msec = _time % 1000;
#ifdef _MSC_VER
			localtime_s(&t, &ts);
#else
			t = *localtime(&ts);
#endif
		}

		/*!
		 * \brief 获取日期
		 * \return YYYYMMDD格式的日期
		 */
		uint32_t date()
		{
			return (t.tm_year+1900)*10000 + (t.tm_mon+1)*100 + t.tm_mday;
		}

		/*!
		 * \brief 获取时间（不含毫秒）
		 * \return HHMMSS格式的时间
		 */
		uint32_t time()
		{
			return t.tm_hour*10000 + t.tm_min*100 + t.tm_sec;
		}

		/*!
		 * \brief 获取时间（含毫秒）
		 * \return HHMMSSMMM格式的时间
		 */
		uint32_t time_ms()
		{
			return t.tm_hour*10000000 + t.tm_min*100000 + t.tm_sec*1000 + _msec;
		}

		/*!
		 * \brief 格式化输出时间字符串
		 * \param buff 输出缓冲区
		 * \param length 缓冲区长度
		 * \param bWithMSec 是否包含毫秒，默认为true
		 * 
		 * \details 将时间格式化为字符串输出到指定缓冲区
		 */
		void format_to(char* buff, uint32_t length, bool bWithMSec = true)
		{
			if (bWithMSec)
				snprintf(buff + length, 8, ",%03u", _msec);
		}

	protected:
		struct tm t;        ///< 时间结构
		uint32_t _msec;     ///< 毫秒部分
	};

	/*!
	 * \brief 高精度计时器类
	 * 
	 * \details 基于chrono库的高精度计时器，用于性能测量和时间差计算。
	 *          支持秒、毫秒、微秒、纳秒级别的时间测量。
	 */
	class Ticker
	{
	public:
		/*!
		 * \brief 构造函数
		 * 
		 * \details 创建计时器并开始计时
		 */
		Ticker()
		{
			_tick = std::chrono::high_resolution_clock::now();
		}

		/*!
		 * \brief 重置计时器
		 * 
		 * \details 重新开始计时
		 */
		void reset()
		{
			_tick = std::chrono::high_resolution_clock::now();
		}

		/*!
		 * \brief 获取经过的秒数
		 * \return 自创建或重置以来经过的秒数
		 */
		inline int64_t seconds() const 
		{
			auto now = std::chrono::high_resolution_clock::now();
			auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now - _tick);
			return elapsed.count();
		}

		/*!
		 * \brief 获取经过的毫秒数
		 * \return 自创建或重置以来经过的毫秒数
		 */
		inline int64_t milli_seconds() const
		{
			auto now = std::chrono::high_resolution_clock::now();
			auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - _tick);
			return elapsed.count();
		}

		/*!
		 * \brief 获取经过的微秒数
		 * \return 自创建或重置以来经过的微秒数
		 */
		inline int64_t micro_seconds() const
		{
			auto now = std::chrono::high_resolution_clock::now();
			auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(now - _tick);
			return elapsed.count();
		}

		/*!
		 * \brief 获取经过的纳秒数
		 * \return 自创建或重置以来经过的纳秒数
		 */
		inline int64_t nano_seconds() const
		{
			auto now = std::chrono::high_resolution_clock::now();
			auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - _tick);
			return elapsed.count();
		}

	private:
		std::chrono::time_point<std::chrono::high_resolution_clock> _tick; ///< 开始计时的时间点
	};
};
